package ch.kerbtier.malurus.renderers;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;

import ch.kerbtier.malurus.Component;
import ch.kerbtier.malurus.Renderer;
import ch.kerbtier.malurus.RenderProvider;
import ch.kerbtier.malurus.RequestContext;

public class VelocityRenderProvider implements RenderProvider {
	private VelocityEngine ve = null;
	private String postfix;
	private File path;

	public VelocityRenderProvider(String path, String postfix) {
		this.postfix = postfix;
		this.path = new File(path);

		ve = new VelocityEngine();
		ve.addProperty("resource.loader", "author");

		ve
				.addProperty("author.resource.loader.class",
						"org.apache.velocity.runtime.resource.loader.FileResourceLoader");
		ve.addProperty("author.resource.loader.path", path);
		ve.addProperty("author.resource.loader.cache", "true");
		ve.addProperty("author.resource.loader.modificationCheckInterval", "5");
		ve.addProperty("runtime.log.logsystem.class", "org.apache.velocity.runtime.log.JdkLogChute");
		try {
			ve.init();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public Renderer getRenderer(String component, String realisation) {
		String name = component.getClass().getCanonicalName() + (realisation == null ? "" : "->" + realisation)
				+ postfix;
		File f = new File(path, name);
		if (f.exists()) {
			return new VelocityRenderer(name, this);
		}
		return null;
	}

	protected VelocityEngine getVe() {
		return ve;
	}

	public class VelocityRenderer implements Renderer {
		private String name;
		private VelocityRenderProvider provider;

		public VelocityRenderer(String name, VelocityRenderProvider provider) {
			this.name = name;
			this.provider = provider;
		}

		public void render(Component component, final RequestContext requestContext) {
			if (component.isVisible()) {
				try {
					Template t = ve.getTemplate(name);
					VelocityContext context = new VelocityContext();
					context.put("classname", component.getClass().getCanonicalName());
					context.put("subject", component);
					context.put("request", requestContext.getRequest());
					context.put("response", requestContext.getResponse());
					context.put("renderer", new Rimpl() {
						public String render(Component component) {
							PrintWriter writer = requestContext.getWriter();
							StringWriter sw = new StringWriter();
							PrintWriter pw = new PrintWriter(sw);

							requestContext.setWriter(pw);
							requestContext.render(component);
							requestContext.setWriter(writer);

							pw.flush();
							return sw.getBuffer().toString();
						}
					});
					t.merge(context, requestContext.getWriter());
				} catch (ResourceNotFoundException e) {
					e.printStackTrace();
				} catch (ParseErrorException e) {
					e.printStackTrace();
				} catch (MethodInvocationException e) {
					e.printStackTrace();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	public interface Rimpl {
		String render(Component component);
	}
}
